/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "lfs.h"
#include "los_typedef.h"
#include "los_fs.h"

// variables used by the filesystem
static lfs_t lfs;
#define IOCTL_ERASE_SECTOR              200
#define IOCTL_ERASE_PAGE                204

#if 0
    return (g_partitionCfg.readFunc)((int)c->context, &addr, dst, size);
}

WEAK int littlefs_block_write(const struct lfs_config *c, lfs_block_t block,
                              lfs_off_t off, const void *dst, lfs_size_t size)
{
    UINT32 addr = c->block_size * block + off;
    UINT32 startaddr = LfsGetStartAddr((int)c->context);
    if (startaddr == INVALID_DEVICE_ADDR) {
        return -1;
    }

    addr += startaddr;

    return (g_partitionCfg.writeFunc)((int)c->context, &addr, dst, size);
}

WEAK int littlefs_block_erase(const struct lfs_config *c, lfs_block_t block)
{
    UINT32 addr = c->block_size * block;
    UINT32 startaddr = LfsGetStartAddr((int)c->context);
    if (startaddr == INVALID_DEVICE_ADDR) {
        return -1;
    }

    addr += startaddr;

    return (g_partitionCfg.eraseFunc)((int)c->context, addr, c->block_size);
}


struct PartitionCfg {
    /* partition low-level read func */
    int  (*readFunc)(int partition, UINT32 *offset, void *buf, UINT32 size);
    /* partition low-level write func */
    int  (*writeFunc)(int partition, UINT32 *offset, const void *buf, UINT32 size);
    /* partition low-level erase func */
    int  (*eraseFunc)(int partition, UINT32 offset, UINT32 size);

    int readSize;       /* size of a block read */
    int writeSize;      /* size of a block write */
    int blockSize;      /* size of an erasable block */
    int blockCount;     /* number of partition blocks */
    int cacheSize;      /* size of block caches */

    int partNo;         /* partition number */
    int lookaheadSize;  /* lookahead size */
    int blockCycles;    /* block cycles */
};
#endif



static int lfs_flash_block_read(int partition, UINT32 * offset, void *buffer, UINT32 size)
{
	/* printf("rr part:%d  addr:%x  size:%d",partition,*offset,size); */
    norflash_read(NULL, buffer, size, *offset);
    return 0;
}

static int lfs_flash_block_prog(int partition, UINT32 * offset, void *buffer, UINT32 size)
{
	/* printf("ww part:%d  addr:%x  size:%d",partition,*offset,size); */
    norflash_write(NULL, (void *)buffer, size,  *offset);
    return 0;
}

static int lfs_flash_block_erase(int partition,UINT32 offset,UINT32 size)
{
	/* printf("ee part:%d  addr:%x  size:%d",partition,offset,size); */
    norflash_ioctl(NULL, IOCTL_ERASE_PAGE,  offset);
    return 0;
}
static int lfs_flash_block_sync(const struct lfs_config *c)
{
    return 0;
}

struct sys_time {
    u16 year;
    u8 month;
    u8 day;
    u8 hour;
    u8 min;
    u8 sec;
};
struct vfs_attr {
    u8 attr;
    u32 fsize;
    u32 sclust;
    struct sys_time crt_time;
    struct sys_time wrt_time;
    struct sys_time acc_time;
};

static int littlefs_flash_init(struct PartitionCfg * cfg)
{
    u32 lfs_addr, lfs_space;
    void *profile_fp = sdfile_open("mnt/sdfile/app/exif", "r");
    if (profile_fp == NULL) {
        puts("lfs_mount ERROR!!!\r\n");
        return -1;
    }
    struct vfs_attr file_attr;
    sdfile_get_attrs(profile_fp, &file_attr);
    lfs_addr = sdfile_cpu_addr2flash_addr(file_attr.sclust);
    lfs_space = file_attr.fsize;
    sdfile_close(profile_fp);
    printf("LFS_addr = 0x%x, size = 0x%x \r\n", lfs_addr, lfs_space);




    // mount the filesystem
    /* cfg->partNo = (void *)lfs_addr; */
	cfg->partNo = 0;//(void *)lfs_addr;
    cfg->blockCount = lfs_space / cfg->blockSize;
    cfg->lookaheadSize = (cfg->blockCount / 8) + (((cfg->blockCount / 8) & 7) ?
        ((8) - ((cfg->blockCount / 8) & 7)) : 0);

    int ret = LOS_DiskPartition(NULL, "littlefs", &lfs_space, &lfs_addr, 1);
    printf("%s: DiskPartition %s\n", __func__, (ret == 0) ? "succeed" : "failed");
    if (ret != 0) {
        return -1;
    }

    return 0;
}

#if 1
#include <sys/mount.h>
#include "los_config.h"
#include "hdf_log.h"
#include "hdf_device_desc.h"
#include "device_resource_if.h"
#include "lfs_adapter.h"

#define LFS_CFG_READ_SIZE       1
#define LFS_CFG_PROG_SIZE       256
#define LFS_CFG_BLOCK_SIZE       256
#define LFS_CFG_CACHE_SIZE      1024
#define LFS_CFG_BLOCK_CYCLES    500

struct fs_cfg {
    char *mount_point;
	struct PartitionCfg lfs_cfg;
    /* struct lfs_config lfs_cfg; */
};

static struct fs_cfg fs[LOSCFG_LFS_MAX_MOUNT_SIZE] = {0};

static uint32_t FsGetResource(struct fs_cfg *fs, const struct DeviceResourceNode *resourceNode)
{
    struct DeviceResourceIface *resource = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
    if (resource == NULL) {
        HDF_LOGE("Invalid DeviceResourceIface");
        return HDF_FAILURE;
    }
    int32_t num = resource->GetElemNum(resourceNode, "mount_points");
    if (num < 0 || num > LOSCFG_LFS_MAX_MOUNT_SIZE) {
        HDF_LOGE("%s: invalid mount_points num %d", __func__, num);
        return HDF_FAILURE;
    }
    for (int32_t i = 0; i < num; i++) {
        if (resource->GetStringArrayElem(resourceNode, "mount_points", i, &fs[i].mount_point, NULL) != HDF_SUCCESS) {
            HDF_LOGE("%s: failed to get mount_points", __func__);
            return HDF_FAILURE;
        }
        /* HDF_LOGD("%s: fs[%d] mount_point=%s, partition=%u, block_size=%u, block_count=%u", __func__, i, */
                 /* fs[i].mount_point, (uint32_t)fs[i].lfs_cfg.context, fs[i].lfs_cfg.block_size, */
                 /* fs[i].lfs_cfg.block_count); */
    }
    return HDF_SUCCESS;
}

static int32_t FsDriverInit(struct HdfDeviceObject *object)
{
    struct FileOpInfo *fileOpInfo = NULL;

    if (object == NULL) {
        return HDF_FAILURE;
    }
    if (object->property) {
        if (FsGetResource(fs, object->property) != HDF_SUCCESS) {
            HDF_LOGE("%s: FsGetResource failed", __func__);
            return HDF_FAILURE;
        }
    }

    for (int i = 0; i < sizeof(fs) / sizeof(fs[0]); i++) {
        if (fs[i].mount_point == NULL) {
            continue;
        }
        fs[i].lfs_cfg.readFunc  = lfs_flash_block_read;
        fs[i].lfs_cfg.writeFunc  = lfs_flash_block_prog;
        fs[i].lfs_cfg.eraseFunc = lfs_flash_block_erase;
        /* fs[i].lfs_cfg.sync  = lfs_flash_block_sync; */

        fs[i].lfs_cfg.readSize = LFS_CFG_READ_SIZE;
        fs[i].lfs_cfg.writeSize = LFS_CFG_PROG_SIZE;
        fs[i].lfs_cfg.cacheSize = LFS_CFG_CACHE_SIZE;
        fs[i].lfs_cfg.blockSize = 256;
        fs[i].lfs_cfg.lookaheadSize = 256;
        fs[i].lfs_cfg.blockCycles = LFS_CFG_BLOCK_CYCLES;
		printf("read_size:%d  prog_size;%d\n",fs[i].lfs_cfg.readSize,fs[i].lfs_cfg.writeSize);
		/* printf("%s   %d\n",__func__,__LINE__); */
        littlefs_flash_init(&fs[i].lfs_cfg);
		/* printf("%s   %d\n",__func__,__LINE__); */
	
		int ret = mount(NULL, fs[i].mount_point, "littlefs", 0, &fs[i].lfs_cfg);
        HDF_LOGI("%s: mount fs on '%s' %s\n", __func__, fs[i].mount_point, (ret == 0) ? "succeed" : "failed");
        if (ret == 0) {
            ret = mkdir(fs[i].mount_point, S_IRWXU | S_IRWXG | S_IRWXO);
            if (ret == 0) {
                HDF_LOGI("create root dir success.");
            } else if (errno == EEXIST) {
                HDF_LOGI("root dir exist.");
            } else {
                HDF_LOGI("create root dir failed.");
            }
        }
    }

    return HDF_SUCCESS;
}

static int32_t FsDriverBind(struct HdfDeviceObject *device)
{
    (void)device;
    return HDF_SUCCESS;
}

static void FsDriverRelease(struct HdfDeviceObject *device)
{
    (void)device;
}

static struct HdfDriverEntry g_fsDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_FS_LITTLEFS",
    .Bind = FsDriverBind,
    .Init = FsDriverInit,
    .Release = FsDriverRelease,
};

HDF_INIT(g_fsDriverEntry);
#endif
